﻿using System.Linq;
using System.Text;

namespace System.Collections.Generic
{
    /// <summary>
    /// <see cref="IEnumerable{T}"/> extension methods.
    /// </summary>
    public static class EnumerableExtension
    {
        #region Boolean 判断

        /// <summary>
        /// Returns true if the <paramref name="source"/> is null or without any items.
        /// </summary>
        public static bool IsNullOrEmpty<T>(this IEnumerable<T> source)
        {
            return (source == null || ((source as ICollection) != null ? source.Count() <= 0 : !source.Any()));
        }

        /// <summary>
        /// Returns true if the <paramref name="source"/> is contains at least one item.
        /// </summary>
        public static bool IsNotNullOrEmpty<T>(this IEnumerable<T> source)
        {
            return !source.IsNullOrEmpty();
        }

        /// <summary>
        ///   Returns whether the sequence contains a certain amount of elements.
        /// </summary>
        /// <typeparam name = "T">The type of the elements of the input sequence.</typeparam>
        /// <param name = "source">The source for this extension method.</param>
        /// <param name = "count">The amount of elements the sequence should contain.</param>
        /// <returns>True when the sequence contains the specified amount of elements, false otherwise.</returns>
        public static bool HasCountOf<T>(this IEnumerable<T> source, int count)
        {
            return source.Take(count + 1).Count() == count;
        }
        #endregion Boolean 判断

        #region Union 并集

        /// <summary>
        /// 拼接两个数据集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="second"></param>
        /// <returns></returns>
        public static IEnumerable<T> Append<T>(this IEnumerable<T> source, IEnumerable<T> second)
        {
            foreach (T t in source) { yield return t; }
            foreach (T t in second) { yield return t; }
        }

        /// <summary>
        /// 对两个序列进行合并。如果其中一个是null，则返回另一个
        /// </summary>
        /// <typeparam name="T">序列类型</typeparam>
        /// <param name="source">源序列</param>
        /// <param name="second">要合并的序列</param>
        /// <returns>合并后的序列</returns>
        public static IEnumerable<T> UnionWith<T>(this IEnumerable<T> source, IEnumerable<T> second)
        {
            if (source == null ^ second == null) return second == null ? source : second;
            return source == null ? null : source.Union(second);
        }

        /// <summary>
        /// 对可遍历数据源进行分页操作
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="source">源</param>
        /// <param name="pagesize">每页尺寸</param>
        /// <param name="executor">执行器</param>
        public static void PagedExecute<T>(this IEnumerable<T> source, int pagesize, Action<int, IEnumerable<T>> executor)
        {
            var count = source.Count();
            var pagecount = count.CeilingDivide(pagesize);

            for (int i = 0; i < pagecount; i++)
            {
                executor(i, source.Skip(pagesize * i).Take(pagesize).ToArray());
            }
        }

        /// <summary>
        /// 将指定的序列生成为树状结构
        /// </summary>
        /// <typeparam name="T">树类型</typeparam>
        /// <typeparam name="TKey">上级键值类型</typeparam>
        /// <param name="src">原始序列</param>
        /// <param name="parentSelector">上级键选择器</param>
        /// <param name="childrenSelector">下级集合选择器</param>
        /// <returns>顶级节点</returns>
        public static IEnumerable<T> PopulateTree<T, TKey>(this IEnumerable<T> src, Func<T, TKey> keySelector, Func<T, TKey> parentSelector, Func<T, IList<T>> childrenSelector, IEqualityComparer<TKey> comparer = null)
            where T : class
        {
            if (src == null)
                throw new ArgumentNullException("src", "src is null.");
            if (keySelector == null)
                throw new ArgumentNullException("parentSelector", "keySelector is null.");
            if (parentSelector == null)
                throw new ArgumentNullException("parentSelector", "parentSelector is null.");
            if (childrenSelector == null)
                throw new ArgumentNullException("childrenSelector", "childrenSelector is null.");
            if (comparer == null)
                comparer = EqualityComparer<TKey>.Default;

            var emptyValue = default(TKey);
            var array = src.ToArray();
            var dicCache = array.ToDictionary(keySelector, comparer);

            var result = array.Where(s => comparer.Equals(parentSelector(s), emptyValue)).ToArray();
            array.ForEach(s =>
            {
                var pkey = parentSelector(s);
                if (comparer.Equals(pkey, emptyValue))
                    return;

                var parent = dicCache.GetValue(pkey);
                if (parent == null)
                    throw new Exception("指定的ParentID无效");

                var children = childrenSelector(parent);
                if (children == null)
                    return;
                children.Add(s);
            });

            return result;
        }

        #endregion Union 并集

        #region Each 遍历

        /// <summary>
        /// 对可遍历对象进行遍历并进行指定操作
        /// </summary>
        /// <typeparam name="T">遍历的类型</typeparam>
        /// <param name="enumberable">对象</param>
        /// <param name="predicate">函数委托</param>
        /// <exception cref="System.ArgumentNullException">predicate</exception>
        public static void ForEach<T>(this IEnumerable<T> enumberable, Action<T> predicate)
        {
            if (enumberable == null)
                throw new ArgumentNullException("enumberable", "enumberable is null.");
            if (predicate == null)
                throw new ArgumentNullException("predicate", "predicate is null.");

            foreach (T item in enumberable)
            {
                predicate(item);
            }
        }

        /// <summary>
        /// 对指定的数据进行条件判断，如果不为空序列则执行
        /// </summary>
        /// <param name="value">值</param>
        /// <param name="action">执行方法</param>
        public static void ExecuteIfNotEmpty<T>(this IEnumerable<T> value, Action<IEnumerable<T>> action)
        {
            if (value != null && value.Any()) action(value);
        }

        /// <summary>
        /// 查找满足条件的首个索引
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="src"></param>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public static int FindIndex<T>(this IEnumerable<T> src, Func<T, bool> predicate)
        {
            if (src == null)
                throw new ArgumentNullException("src", "src is null.");
            if (predicate == null)
                throw new ArgumentNullException("predicate", "predicate is null.");

            var index = 0;
            foreach (var item in src)
            {
                if (predicate(item)) return index;
                index++;
            }

            return -1;
        }

        /// <summary>
        /// 判断数组是否全部满足条件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static bool TrueForEach<T>(this IEnumerable<T> source, Func<T, bool> action)
        {
            return source.All(action);
        }
        #endregion Each 遍历

        #region Join 连接
        /// <summary>
        /// 在集合的末尾插入一个元素
        ///     Appends an element to the end of the current collection and returns the new collection.
        /// </summary>
        /// <typeparam name="T">The enumerable data type</typeparam>
        /// <param name="source">The data values.</param>
        /// <param name="item">The element to append the current collection with.</param>
        /// <returns>
        ///     The modified collection.
        /// </returns>
        /// <example>
        ///     var integers = Enumerable.Range(0, 3);  // 0, 1, 2
        ///     integers = integers.Append(3);          // 0, 1, 2, 3
        /// </example>
        public static IEnumerable<T> Append<T>(this IEnumerable<T> source, T item)
        {
            foreach (var i in source)
                yield return i;

            yield return item;
        }

        /// <summary>
        /// 在集合的开头插入一个元素
        ///     Prepends an element to the start of the current collection and returns the new collection.
        /// </summary>
        /// <typeparam name="T">The enumerable data type</typeparam>
        /// <param name="source">The data values.</param>
        /// <param name="item">The element to prepend the current collection with.</param>
        /// <returns>
        ///     The modified collection.
        /// </returns>
        /// <example>
        ///     var integers = Enumerable.Range(1, 3);  // 1, 2, 3
        ///     integers = integers.Prepend(0);         // 0, 1, 2, 3
        /// </example>
        public static IEnumerable<T> Prepend<T>(this IEnumerable<T> source, T item)
        {
            yield return item;

            foreach (var i in source)
                yield return i;
        }

        /// <summary>
        /// 将对象序列连接为字符串
        /// </summary>
        /// <typeparam name="T">序列类型</typeparam>
        /// <param name="src">源序列</param>
        /// <returns><see cref="T:System.String"/></returns>
        public static string JoinAsString<T>(this IEnumerable<T> src)
        {
            return JoinAsString(src, ",");
        }

        /// <summary>
        /// 使用指定的分隔符将字符串可遍历对象连接起来
        /// </summary>
        /// <typeparam name="T">序列类型</typeparam>
        /// <param name="items">可遍历序列</param>
        /// <param name="seperator">分隔符</param>
        /// <param name="formatString">字符串表示格式</param>
        /// <returns><see cref="T:System.String"/></returns>
        public static string JoinAsString<T>(this IEnumerable<T> items, string separator, string formatString = "")
        {
            if (items == null || items.Count() == 0)
                return string.Empty;

            // shortcut for string enumerable
            if (typeof(T) == typeof(string))
            {
                return string.Join(separator, ((IEnumerable<string>)items).ToArray());
            }

            if (string.IsNullOrEmpty(formatString))
            {
                formatString = "{0}";
            }
            else
            {
                formatString = string.Format("{{0:{0}}}", formatString);
            }

            return string.Join(separator,items.Where(s => s != null).Select(s => string.Format(formatString, s)));
        }

        /// <summary>
        /// 将对象序列通过方法拼接为字符串，分隔符为separator
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="items"></param>
        /// <param name="separator"></param>
        /// <param name="appender"></param>
        /// <returns></returns>
        public static string JoinAsString<T>(this IEnumerable<T> items, string separator, Func<T, string> appender)
        {
            if (items == null || items.Count() == 0)
                return string.Empty;

            return string.Join(separator, items.Where(s => s != null).Select(s => appender(s)));
        }

        /// <summary>
        /// Join values using a delimeter.
        /// </summary>
        /// <typeparam name="T">Type of item to use with the method.</typeparam>
        /// <param name="items">The items.</param>
        /// <param name="delimeter">The delimeter.</param>
        /// <param name="newLineAfterCount">Number of lines after which a new line will be inserted.</param>
        /// <param name="newLineText">New line text to be inserted after each new line is inserted.</param>
        /// <param name="appender">The function to call when appending info.</param>
        /// <returns>String with joined items.</returns>
        public static string JoinDelimitedWithNewLine<T>(this IList<T> items, string delimeter, int newLineAfterCount, string newLineText, Func<T, string> appender)
        {
            if (items == null || items.Count == 0)
                return string.Empty;

            if (items.Count == 1)
                return appender(items[0]);

            StringBuilder buffer = new StringBuilder();
            buffer.Append(appender(items[0]));

            for (int ndx = 1; ndx < items.Count; ndx++)
            {
                T item = items[ndx];
                string append = appender(item);
                if (ndx % newLineAfterCount == 0)
                    buffer.Append(newLineText);

                buffer.Append(delimeter + append);
            }
            return buffer.ToString();
        }

        /// <summary>
        /// 将对象选择为字符串并进行格式化
        /// </summary>
        /// <typeparam name="T">来源类型</typeparam>
        /// <param name="source">来源</param>
        /// <param name="format">格式化字符串</param>
        /// <param name="selector">要填充的参数列</param>
        /// <returns><see cref="IEnumerable{T}"/></returns>
        public static IEnumerable<string> FormatValue<T>(this IEnumerable<T> source, string format, params Func<T, object>[] selector)
        {
            return source.Select(s => string.Format(format, selector.Select(m => m(s)))).ToArray();
        }
        #endregion Join 连接

        #region Convert 转换
        /// <summary>
        /// 集合转换成目标类型
        /// 	Converts all items of a list and returns them as enumerable.
        /// </summary>
        /// <typeparam name = "TSource">The source data type</typeparam>
        /// <typeparam name = "TTarget">The target data type</typeparam>
        /// <param name = "source">The source data.</param>
        /// <returns>The converted data</returns>
        /// <example>
        /// 	var values = new[] { "1", "2", "3" };
        /// 	values.ConvertList&lt;string, int&gt;().ForEach(Console.WriteLine);
        /// </example>
        public static IEnumerable<TTarget> ConvertList<TSource, TTarget>(this IEnumerable<TSource> source)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            return source.Select(value => value.ConvertTo<TTarget>());
        }

        /// <summary>
        /// 转换队列到HashSet对象
        /// </summary>
        /// <typeparam name="T">可枚举类型</typeparam>
        /// <param name="source">源</param>
        /// <returns></returns>
        public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source)
        {
            return new HashSet<T>(source);
        }

        /// <summary>
        /// 转换队列到HashSet对象
        /// </summary>
        /// <typeparam name="T">可枚举类型</typeparam>
        /// <param name="source">源</param>
        /// <param name="comparer">要使用的比较器</param>
        /// <returns><see cref="T:System.Collections.Generic.HashSet{T}"/></returns>
        public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source, IEqualityComparer<T> comparer)
        {
            return new HashSet<T>(source, comparer);
        }

        /// <summary>
        /// 生成两个序列的交叉键值对
        /// </summary>
        /// <typeparam name="T1">序列1类型</typeparam>
        /// <typeparam name="T2">序列2类型</typeparam>
        /// <param name="t1">序列1</param>
        /// <param name="t2">序列2</param>
        /// <returns></returns>
        public static IEnumerable<KeyValuePair<T1, T2>> CrossJoin<T1, T2>(this IEnumerable<T1> t1, IEnumerable<T2> t2)
        {
            foreach (var t in t1)
            {
                foreach (var tt in t2)
                {
                    yield return new KeyValuePair<T1, T2>(t, tt);
                }
            }
        }

        /// <summary>
        /// 将指定的可遍历序列转换为一个队列
        /// </summary>
        /// <typeparam name="T">序列类型</typeparam>
        /// <param name="source">源序列</param>
        /// <returns><see cref="T:System.Collections.Generic.Queue{T}"/></returns>
        public static Queue<T> ToQueue<T>(this IEnumerable<T> source)
        {
            return new Queue<T>(source);
        }

        /// <summary>
        /// 将指定的可遍历序列转换为一个栈
        /// </summary>
        /// <typeparam name="T">序列类型</typeparam>
        /// <param name="source">源序列</param>
        /// <returns><see cref="System.Collections.Generic.Stack{T}"/></returns>
        public static Stack<T> ToStack<T>(this IEnumerable<T> source)
        {
            return new Stack<T>(source);
        }

        ///<summary>
        /// Turn the list of objects to a string of Common Seperated Value
        ///</summary>
        ///<param name="source"></param>
        ///<param name="separator"></param>
        ///<typeparam name="T"></typeparam>
        ///<returns></returns>
        /// <example>
        /// 	<code>
        /// 		var values = new[] { 1, 2, 3, 4, 5 };
        ///			string csv = values.ToCSV(';');
        /// 	</code>
        /// </example>
        /// <remarks>
        /// 	Contributed by Moses, http://mosesofegypt.net
        /// </remarks>
        public static string ToCSV<T>(this IEnumerable<T> source, char separator)
        {
            if (source == null)
                return String.Empty;

            var csv = new StringBuilder();
            source.ForEach(value => csv.AppendFormat("{0}{1}", value, separator));
            return csv.ToString(0, csv.Length - 1);
        }

        ///<summary>
        /// Turn the list of objects to a string of Common Seperated Value
        ///</summary>
        ///<param name="source"></param>
        ///<typeparam name="T"></typeparam>
        ///<returns></returns>
        /// <example>
        /// 	<code>
        /// 		var values = new[] {1, 2, 3, 4, 5};
        ///			string csv = values.ToCSV();
        /// 	</code>
        /// </example>
        /// <remarks>
        /// 	Contributed by Moses, http://mosesofegypt.net
        /// </remarks>
        public static string ToCSV<T>(this IEnumerable<T> source)
        {
            return source.ToCSV(',');
        }

        /// <summary>
        /// 通过特殊转换方式将集合转数组
        ///     Creates an Array from an IEnumerable&lt;T&gt; using the specified transform function.
        /// </summary>
        /// <typeparam name="TSource">The source data type</typeparam>
        /// <typeparam name="TResult">The target data type</typeparam>
        /// <param name="source">The source data.</param>
        /// <param name="selector">A transform function to apply to each element.</param>
        /// <returns>An Array of the target data type</returns>
        /// <example>
        ///     var integers = Enumerable.Range(1, 3);
        ///     var intStrings = values.ToArray(i => i.ToString());
        /// </example>
        /// <remarks>
        ///     This method is a shorthand for the frequently use pattern IEnumerable&lt;T&gt;.Select(Func).ToArray()
        /// </remarks>
        public static TResult[] ToArray<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
        {
            return source.Select(selector).ToArray();
        }

        /// <summary>
        /// 通过特殊方法将集合转List
        ///     Creates a List&lt;T&gt; from an IEnumerable&lt;T&gt; using the specified transform function.
        /// </summary>
        /// <typeparam name="TSource">The source data type</typeparam>
        /// <typeparam name="TResult">The target data type</typeparam>
        /// <param name="source">The source data.</param>
        /// <param name="selector">A transform function to apply to each element.</param>
        /// <returns>An IEnumerable&lt;T&gt; of the target data type</returns>
        /// <example>
        ///     var integers = Enumerable.Range(1, 3);
        ///     var intStrings = values.ToList(i => i.ToString());
        /// </example>
        /// <remarks>
        ///     This method is a shorthand for the frequently use pattern IEnumerable&lt;T&gt;.Select(Func).ToList()
        /// </remarks>
        public static List<TResult> ToList<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
        {
            return source.Select(selector).ToList();
        }

        /// <summary>
        /// Converts an enumeration of groupings into a Dictionary of those groupings.
        /// </summary>
        /// <typeparam name="TKey">Key type of the grouping and dictionary.</typeparam>
        /// <typeparam name="TValue">Element type of the grouping and dictionary list.</typeparam>
        /// <param name="groupings">The enumeration of groupings from a GroupBy() clause.</param>
        /// <returns>A dictionary of groupings such that the key of the dictionary is TKey type and the value is List of TValue type.</returns>
        public static Dictionary<TKey, List<TValue>> ToDictionary<TKey, TValue>(this IEnumerable<IGrouping<TKey, TValue>> groupings)
        {
            return groupings.ToDictionary(group => group.Key, group => group.ToList());
        }
        #endregion //Convert 转换


        #region Select 选择
        /// <summary>
        /// 重载Select方法
        /// Overload the Select to allow null as a return
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="source"></param>
        /// <param name="selector"></param>
        /// <param name="allowNull"></param>
        /// <returns>An <see cref="IEnumerable{TResult}"/> using the selector containing null or non-null results based on <see cref="allowNull"/>.</returns>
        /// <example>
        /// <code>
        /// var list = new List{object}{ new object(), null, null };
        /// var noNulls = list.Select(x => x, false);
        /// </code>
        /// </example>
        /// <remarks>
        /// Contributed by thinktech_coder
        /// </remarks>
        public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector, bool allowNull = true)
        {
            foreach (var item in source)
            {
                var select = selector(item);
                if (allowNull || !Equals(select, default(TSource)))
                    yield return select;
            }
        }

        #endregion

        #region Filter 筛选



        /// <summary>
        /// 将指定的序列转换为目标序列
        /// </summary>
        /// <typeparam name="TIn"></typeparam>
        /// <typeparam name="TOut"></typeparam>
        /// <param name="src"></param>
        /// <param name="targetObj"></param>
        /// <returns></returns>
        public static IEnumerable<TOut> CastToAnonymousType<TIn, TOut>(this IEnumerable<TIn> src, TOut targetObj)
        {
            return src.Cast<TOut>();
        }

     

        /// <summary>
        /// 对队列进行过滤, 去除为空的项目
        /// </summary>
        /// <typeparam name="T">队列类型</typeparam>
        /// <param name="source">来源</param>
        /// <returns></returns>
        public static IEnumerable<T> ExceptNull<T>(this IEnumerable<T> source) where T : class
        {
            return source.Where(s => s != null);
        }

        ///<summary>
        ///	Returns enumerable object based on target, which does not contains null references.
        ///	If target is null reference, returns empty enumerable object.
        ///</summary>
        ///<typeparam name = "T">Type of items in target.</typeparam>
        ///<param name = "target">Target enumerable object. Can be null.</param>
        ///<example>
        ///	object[] items = null;
        ///	foreach(var item in items.NotNull()){
        ///	// result of items.NotNull() is empty but not null enumerable
        ///	}
        /// 
        ///	object[] items = new object[]{ null, "Hello World!", null, "Good bye!" };
        ///	foreach(var item in items.NotNull()){
        ///	// result of items.NotNull() is enumerable with two strings
        ///	}
        ///</example>
        ///<remarks>
        ///	Contributed by tencokacistromy, http://www.codeplex.com/site/users/view/tencokacistromy
        ///</remarks>
        public static IEnumerable<T> IgnoreNulls<T>(this IEnumerable<T> target)
        {
            if (ReferenceEquals(target, null))
                yield break;

            foreach (var item in target.Where(item => !ReferenceEquals(item, null)))
                yield return item;
        }

        /// <summary>
        /// 	Returns the maximum item based on a provided selector.
        /// </summary>
        /// <typeparam name = "TItem">The item type</typeparam>
        /// <typeparam name = "TValue">The value item</typeparam>
        /// <param name = "items">The items.</param>
        /// <param name = "selector">The selector.</param>
        /// <param name = "maxValue">The max value as output parameter.</param>
        /// <returns>The maximum item</returns>
        /// <example>
        /// 	<code>
        /// 		int age;
        /// 		var oldestPerson = persons.MaxItem(p =&gt; p.Age, out age);
        /// 	</code>
        /// </example>
        public static TItem MaxItem<TItem, TValue>(this IEnumerable<TItem> items, Func<TItem, TValue> selector, out TValue maxValue)
            where TItem : class
            where TValue : IComparable
        {
            TItem maxItem = null;
            maxValue = default(TValue);

            foreach (var item in items)
            {
                if (item == null)
                    continue;

                var itemValue = selector(item);

                if ((maxItem != null) && (itemValue.CompareTo(maxValue) <= 0))
                    continue;

                maxValue = itemValue;
                maxItem = item;
            }

            return maxItem;
        }

        /// <summary>
        /// 	Returns the maximum item based on a provided selector.
        /// </summary>
        /// <typeparam name = "TItem">The item type</typeparam>
        /// <typeparam name = "TValue">The value item</typeparam>
        /// <param name = "items">The items.</param>
        /// <param name = "selector">The selector.</param>
        /// <returns>The maximum item</returns>
        /// <example>
        /// 	<code>
        /// 		var oldestPerson = persons.MaxItem(p =&gt; p.Age);
        /// 	</code>
        /// </example>
        public static TItem MaxItem<TItem, TValue>(this IEnumerable<TItem> items, Func<TItem, TValue> selector)
            where TItem : class
            where TValue : IComparable
        {
            TValue maxValue;

            return items.MaxItem(selector, out maxValue);
        }

        /// <summary>
        /// 	Returns the minimum item based on a provided selector.
        /// </summary>
        /// <typeparam name = "TItem">The item type</typeparam>
        /// <typeparam name = "TValue">The value item</typeparam>
        /// <param name = "items">The items.</param>
        /// <param name = "selector">The selector.</param>
        /// <param name = "minValue">The min value as output parameter.</param>
        /// <returns>The minimum item</returns>
        /// <example>
        /// 	<code>
        /// 		int age;
        /// 		var youngestPerson = persons.MinItem(p =&gt; p.Age, out age);
        /// 	</code>
        /// </example>
        public static TItem MinItem<TItem, TValue>(this IEnumerable<TItem> items, Func<TItem, TValue> selector, out TValue minValue)
            where TItem : class
            where TValue : IComparable
        {
            TItem minItem = null;
            minValue = default(TValue);

            foreach (var item in items)
            {
                if (item == null)
                    continue;
                var itemValue = selector(item);

                if ((minItem != null) && (itemValue.CompareTo(minValue) >= 0))
                    continue;
                minValue = itemValue;
                minItem = item;
            }

            return minItem;
        }

        /// <summary>
        /// 	Returns the minimum item based on a provided selector.
        /// </summary>
        /// <typeparam name = "TItem">The item type</typeparam>
        /// <typeparam name = "TValue">The value item</typeparam>
        /// <param name = "items">The items.</param>
        /// <param name = "selector">The selector.</param>
        /// <returns>The minimum item</returns>
        /// <example>
        /// 	<code>
        /// 		var youngestPerson = persons.MinItem(p =&gt; p.Age);
        /// 	</code>
        /// </example>
        public static TItem MinItem<TItem, TValue>(this IEnumerable<TItem> items, Func<TItem, TValue> selector)
            where TItem : class
            where TValue : IComparable
        {
            TValue minValue;
            return items.MinItem(selector, out minValue);
        }

        /// <summary>
        /// Returns the first item or the <paramref name="defaultValue"/> if the <paramref name="source"/>
        /// does not contain any item.
        /// </summary>
        public static T FirstOrDefault<T>(this IEnumerable<T> source, T defaultValue)
        {
            return (source.IsNotNullOrEmpty() ? source.First() : defaultValue);
        }

        ///<summary>
        /// 返回消除重复项 Get Distinct
        ///</summary>
        ///<param name = "source"></param>
        ///<param name = "expression"></param>
        ///<typeparam name = "T"></typeparam>
        ///<typeparam name = "TKey"></typeparam>
        ///<returns></returns>
        /// <remarks>
        /// 	Contributed by Michael T, http://about.me/MichaelTran
        /// </remarks>
        public static IEnumerable<T> Distinct<T, TKey>(this IEnumerable<T> source, Func<T, TKey> expression)
        {
            return source == null ? Enumerable.Empty<T>() : source.GroupBy(expression).Select(i => i.First());
        }
        #endregion //Filter 筛选

        #region Remove 移除

        /// <summary>
        /// Removes matching items from a sequence
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">The source.</param>
        /// <param name="predicate">The predicate.</param>
        /// <returns></returns>
        /// 
        /// <remarks>
        /// 	Renamed by James Curran, to match corresponding HashSet.RemoveWhere()
        /// 	</remarks>

        public static IEnumerable<T> RemoveWhere<T>(this IEnumerable<T> source, Predicate<T> predicate)
        {
            if (source == null)
                yield break;

            foreach (T t in source)
                if (!predicate(t))
                    yield return t;
        }

        ///<summary>
        ///	Remove item from a list
        ///</summary>
        ///<param name = "source"></param>
        ///<param name = "predicate"></param>
        ///<typeparam name = "T"></typeparam>
        ///<returns></returns>
        /// <remarks>
        /// 	Contributed by Michael T, http://about.me/MichaelTran
        /// </remarks>
        [Obsolete("Use RemoveWhere instead..")]
        public static IEnumerable<T> RemoveAll<T>(this IEnumerable<T> source, Predicate<T> predicate)
        {
            if (source == null)
                return Enumerable.Empty<T>();

            var list = source.ToList();
            list.RemoveAll(predicate);
            return list;
        }
        #endregion Remove 移除

        #region Sum 统计
        /// <summary>
        /// Computes the sum of a sequence of UInt32 values.
        /// </summary>
        /// <param name="source">A sequence of UInt32 values to calculate the sum of.</param>
        /// <returns>The sum of the values in the sequence.</returns>
        public static uint Sum(this IEnumerable<uint> source)
        {
            return source.Aggregate(0U, (current, number) => current + number);
        }

        /// <summary>
        /// Computes the sum of a sequence of UInt64 values.
        /// </summary>
        /// <param name="source">A sequence of UInt64 values to calculate the sum of.</param>
        /// <returns>The sum of the values in the sequence.</returns>
        public static ulong Sum(this IEnumerable<ulong> source)
        {
            return source.Aggregate(0UL, (current, number) => current + number);
        }

        /// <summary>
        /// Computes the sum of a sequence of nullable UInt32 values.
        /// </summary>
        /// <param name="source">A sequence of nullable UInt32 values to calculate the sum of.</param>
        /// <returns>The sum of the values in the sequence.</returns>
        public static uint? Sum(this IEnumerable<uint?> source)
        {
            return source.Where(nullable => nullable.HasValue).Aggregate(0U, (current, nullable) => current + nullable.GetValueOrDefault());
        }

        /// <summary>
        /// Computes the sum of a sequence of nullable UInt64 values.
        /// </summary>
        /// <param name="source">A sequence of nullable UInt64 values to calculate the sum of.</param>
        /// <returns>The sum of the values in the sequence.</returns>
        public static ulong? Sum(this IEnumerable<ulong?> source)
        {
            return source.Where(nullable => nullable.HasValue).Aggregate(0UL, (current, nullable) => current + nullable.GetValueOrDefault());
        }

        /// <summary>
        /// Computes the sum of a sequence of UInt32 values that are obtained by invoking a transformation function on each element of the intput sequence.
        /// </summary>
        /// <param name="source">A sequence of values that are used to calculate a sum.</param>
        /// <param name="selection">A transformation function to apply to each element.</param>
        /// <returns>The sum of the projected values.</returns>
        public static uint Sum<T>(this IEnumerable<T> source, Func<T, uint> selection)
        {
            return ElementsNotNullFrom(source).Select(selection).Sum();
        }

        /// <summary>
        /// Computes the sum of a sequence of nullable UInt32 values that are obtained by invoking a transformation function on each element of the intput sequence.
        /// </summary>
        /// <param name="source">A sequence of values that are used to calculate a sum.</param>
        /// <param name="selection">A transformation function to apply to each element.</param>
        /// <returns>The sum of the projected values.</returns>
        public static uint? Sum<T>(this IEnumerable<T> source, Func<T, uint?> selection)
        {
            return ElementsNotNullFrom(source).Select(selection).Sum();
        }

        /// <summary>
        /// Computes the sum of a sequence of UInt64 values that are obtained by invoking a transformation function on each element of the intput sequence.
        /// </summary>
        /// <param name="source">A sequence of values that are used to calculate a sum.</param>
        /// <param name="selector">A transformation function to apply to each element.</param>
        /// <returns>The sum of the projected values.</returns>
        public static ulong Sum<T>(this IEnumerable<T> source, Func<T, ulong> selector)
        {
            return ElementsNotNullFrom(source).Select(selector).Sum();
        }

        /// <summary>
        /// Computes the sum of a sequence of nullable UInt64 values that are obtained by invoking a transformation function on each element of the intput sequence.
        /// </summary>
        /// <param name="source">A sequence of values that are used to calculate a sum.</param>
        /// <param name="selector">A transformation function to apply to each element.</param>
        /// <returns>The sum of the projected values.</returns>
        public static ulong? Sum<T>(this IEnumerable<T> source, Func<T, ulong?> selector)
        {
            return ElementsNotNullFrom(source).Select(selector).Sum();
        }

        private static IEnumerable<T> ElementsNotNullFrom<T>(IEnumerable<T> source)
        {
            return source.Where(x => x.IsNotNull());
        }
        #endregion

        /// <summary>
        /// 将源数据按指定的尺寸分割，并返回最终的结果
        /// </summary>
        /// <typeparam name="T">源类型</typeparam>
        /// <param name="source">来源数据</param>
        /// <param name="pageIndex">当前页</param>
        /// <param name="pageSize">每页尺寸</param>
        /// <param name="totalCount">总条数</param>
        /// <returns>包含了每页数据的列表</returns>
        /// <exception cref="ArgumentOutOfRangeException"><c>pagesize</c> is out of range.</exception>
        /// <exception cref="ArgumentNullException">source</exception>
        public static List<T[]> SplitPage<T>(this IEnumerable<T> source, int pageIndex, int pageSize, out int totalCount)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (pageSize < 1)
                throw new ArgumentOutOfRangeException("pagesize");

            var index = (pageIndex - 1) * pageSize;
            totalCount = source.Count();
            var result = new List<T[]>(totalCount.CeilingDivide(pageSize));

            while (index < totalCount)
            {
                result.Add(source.Skip(index).Take(pageSize).ToArray());
                index += pageSize;
            }

            return result;
        }

        /// <summary>
        /// 将多个对象同时压入队列
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="queue">队列</param>
        /// <param name="eles">对象</param>
        public static void EnqueueMany<T>(this Queue<T> queue, IEnumerable<T> eles)
        {
            eles.ForEach(queue.Enqueue);
        }
    }
}